# 下面是闭包函数示例
'''
闭包和装饰器的简单示例
注意nonlocal关键字的使用
和global关键字的使用
'''
def outer():
    '''
    外部函数
    :return: 内部函数的引用
    其内部函数可以访问外部函数的变量，即使外部函数已经返回
    其内部函数就是一个闭包
    在这个例子中，闭包的作用是：
    1. 保存了外部函数的局部变量 x 的值，即使外部函数已经返回
    2. 可以在外部函数返回后，仍然可以访问和修改 x 的值
    
    '''
    x = 10  # 外部函数的局部变量

    def inner():
        nonlocal x  # 使用 nonlocal 关键字声明 x 是外部函数的局部变量
        x += 1  # 修改外部函数的局部变量
        print("Inner function:", x)  # 打印内部函数的值

    return inner  # 返回内部函数的引用


closure = outer()  # 创建闭包
closure()  # 调用闭包，输出 "Inner function: 11"
closure()  # 再次调用闭包，输出 "Inner function: 12"
closure()  # 再次调用闭包，输出 "Inner function: 13"

# 一个装饰器示例，用于计算函数的运行时间
# 装饰器是一种特殊的函数，它可以接受一个函数作为参数，并返回一个新的函数。
# 新的函数可以在原函数执行前后添加一些额外的功能
import time
def timer(func):
    '''
    装饰器函数
    :param func: 被装饰的函数
    :return: 新的函数
    其内部函数可以在原函数执行前后添加一些额外的功能
    在这个例子中，装饰器的作用是：
    1. 计算函数的运行时间
    2. 打印函数的运行时间
    3. 返回原函数的返回值
    '''
    def wrapper(*args, **kwargs):  # 定义一个新的函数，用于包装原函数 这是一个闭包函数，它可以访问外部函数的变量。
        start_time = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 调用原函数
        end_time = time.time()  # 记录结束时间
        times = (end_time - start_time)*1000  # 计算运行时间
        # 打印函数的运行时间
        print(f"Function {func.__name__} took {times:.4f} ms to run.")
        return result  # 返回原函数的返回值
    return wrapper  # 返回新的函数

# 下面是一个使用装饰器的示例    
@timer  # 使用装饰器，将函数包装成新的函数，新函数可以在原函数执行前后添加一些额外的功能
        # 在函数名前加上 @ 符号，就可以将函数包装成新的函数，新函数可以在原函数执行前后添加一些额外的功能
        # 在调用下面名字的函数时，实际上调用了@后名字的函数，而不是原函数
        # 当然timer函数的形参,是原来的函数
        # 执行@后名字的函数时，会执行到其内函数，这里是闭包函数wrapper，
        # 执行一些功能，最后返回的是闭包函数wrapper的引用，当然闭包函数中又调用了原来的函数，使用其功能执行。
        # 所以调用@后名字的函数时，实际上调用了闭包函数wrapper，
         
def say_hello(name):  # 定义一个函数，用于打印问候语
    '''
    打印问候语
    :param name: 姓名
    :return: 无
    '''
    print(f"Hello, {name}!")
say_hello("Alice")  # 调用函数，输出 "Hello, Alice!"，并打印函数的运行时间
say_hello("Bob")  # 调用函数，输出 "Hello, Bob!"，并打印函数的运行时间
say_hello("Charlie")  # 调用函数，输出 "Hello, Charlie!"，并打印函数的运行时间